package du.myseckill.service.Impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import du.myseckill.dao.GoodsDao;
import du.myseckill.dao.UserSecKillDao;
import du.myseckill.dto.Exposer;
import du.myseckill.dto.SecKillMsg;
import du.myseckill.dto.SeckillExcecution;
import du.myseckill.enums.SecKillStatEnum;
import du.myseckill.exception.ServerException;
import du.myseckill.pojo.Goods;
import du.myseckill.pojo.UserSecKill;
import du.myseckill.service.GoodsService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.util.Date;
import java.util.List;

import static du.myseckill.Const.RedisPrefix.*;

@Service
public class GoodsServiceImpl implements GoodsService, InitializingBean {
    @Autowired
    private GoodsDao goodsDao;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserSecKillDao userSecKillDao;
    @Autowired
    private SecKillMQProducer secKillMQProducer;

    private final String slat = "hdlqhndwuhquhw@%^U%7878007sadwa";

    /**
     * 查询商品列表
     *
     * @return
     */
    public List<Goods> getGoodsList() {
        return goodsDao.selectList(null);
    }


    /**
     * 获取商品详情
     *
     * @param userId
     * @return
     */
    public Goods getGoodsById(long userId) {
        return goodsDao.selectById(userId);
    }

    /**
     * 暴露接口
     *
     * @param goodsId
     * @return
     */
    public Exposer exposeSeckillUrl(long goodsId) {

        Goods goods = JSON.parseObject(JSON.toJSONString(redisTemplate.boundHashOps(GOODS_LIST).get(GOODS_INFO + goodsId)), Goods.class);
        if (goods == null) {
            goods = getGoodsById(goodsId);
            if (goods == null) {
                return new Exposer(false, goodsId);
            } else {
                redisTemplate.boundHashOps(GOODS_LIST).put(GOODS_INFO + goodsId, goods);
            }
        }

        Date now = new Date();
        Date startTime = goods.getStartTime();
        Date endTime = goods.getEndTime();
        if (now.getTime() < startTime.getTime() || now.getTime() > endTime.getTime()) {
            return new Exposer(false, goodsId, now.getTime(), startTime.getTime(), endTime.getTime());
        }
        String md5 = getMD5(goodsId);
        return new Exposer(true, md5, goodsId);

    }

    private String getMD5(long seckillId) {
        String base = seckillId + "/" + slat;
        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
        return md5;
    }

    //数据库悲观锁实现
    @Transactional(rollbackFor = Exception.class)
    public SeckillExcecution executeSecKill(long goodsId, Long userId, Long userPhone, String md5) throws ServerException {
        if (md5 == null || !md5.equals(getMD5(goodsId))) {
            throw new ServerException("seckill data rewrite");
        } else {
            if(userPhone==null||userId==null){
                return new SeckillExcecution(goodsId, SecKillStatEnum.LOGOUT);
            }
//            Goods goods = JSON.parseObject(JSON.toJSONString(redisTemplate.boundHashOps(GOODS_LIST).get(GOODS_INFO + goodsId)), Goods.class);
//            if (goods == null) {
//                goods = getGoodsById(goodsId);
//                if (goods == null) {
//                    return new SeckillExcecution(goodsId, SecKillStatEnum.INNER_ERROR);
//                } else {
//                    redisTemplate.boundHashOps(GOODS_LIST).put(GOODS_INFO + goodsId, goods);
//                }
//            }
            Goods goods = goodsDao.selectById(goodsId);
            Date date = new Date();
            //查库存
            int goodsNumber = goods.getGoodsNumber();
            if(goodsNumber<=0){
                return new SeckillExcecution(goodsId, SecKillStatEnum.END);
            }
            //减库存
            int update = goodsDao.updateGoodsNumber(goodsId);

            if(update==1){
                //插入秒杀记录
                int insertUserSecKill = goodsDao.insertUserSecKill(userId, goodsId);
                if(insertUserSecKill==1){
                    return new SeckillExcecution(goodsId,SecKillStatEnum.SUCCESS);
                }else {
                    return new SeckillExcecution(goodsId,SecKillStatEnum.REPEAT_KILL);
                }
            }else {
                return new SeckillExcecution(goodsId, SecKillStatEnum.END);
            }
        }

    }

    //AOP+ReentrantLock实现
    @Transactional(rollbackFor = Exception.class)
    public SeckillExcecution executeSecKillAOPLock(long goodsId, Long userId, Long userPhone, String md5) throws ServerException {
        System.out.println("执行秒杀！");
        if (md5 == null || !md5.equals(getMD5(goodsId))) {
            throw new ServerException("seckill data rewrite");
        } else {
            if(userPhone==null||userId==null){
                return new SeckillExcecution(goodsId, SecKillStatEnum.LOGOUT);
            }
//            Goods goods = JSON.parseObject(JSON.toJSONString(redisTemplate.boundHashOps(GOODS_LIST).get(GOODS_INFO + goodsId)), Goods.class);
//            if (goods == null) {
//                goods = getGoodsById(goodsId);
//                if (goods == null) {
//                    return new SeckillExcecution(goodsId, SecKillStatEnum.INNER_ERROR);
//                } else {
//                    redisTemplate.boundHashOps(GOODS_LIST).put(GOODS_INFO + goodsId, goods);
//                }
//            }

            Goods goods = goodsDao.selectById(goodsId);
            Date date = new Date();
            //查库存
            int goodsNumber = goods.getGoodsNumber();
            if(goodsNumber==0){
                return new SeckillExcecution(goodsId, SecKillStatEnum.END);
            }
            //减库存
            goods.setGoodsNumber(goods.getGoodsNumber()-1);
            int update = goodsDao.update(goods, new LambdaQueryWrapper<Goods>()
                    .eq(Goods::getGoodsId, goodsId)
                    .lt(Goods::getStartTime, date)
                    .gt(Goods::getEndTime, date));
            if(update==1){
                //插入秒杀记录
                int insertUserSecKill = goodsDao.insertUserSecKill(userId, goodsId);
                if(insertUserSecKill==1){

                    return new SeckillExcecution(goodsId,SecKillStatEnum.SUCCESS);
                }else {
                    return new SeckillExcecution(goodsId,SecKillStatEnum.REPEAT_KILL);
                }
            }else {
                return new SeckillExcecution(goodsId, SecKillStatEnum.END);
            }
        }

    }


    //悲观锁实现
    @Transactional(rollbackFor = Exception.class)
    public SeckillExcecution executeSecKillDBPLock(long goodsId, Long userId, Long userPhone, String md5) throws ServerException {
        System.out.println("执行秒杀！");
        if (md5 == null || !md5.equals(getMD5(goodsId))) {
            throw new ServerException("seckill data rewrite");
        } else {
            if(userPhone==null||userId==null){
                return new SeckillExcecution(goodsId, SecKillStatEnum.LOGOUT);
            }
//            Goods goods = JSON.parseObject(JSON.toJSONString(redisTemplate.boundHashOps(GOODS_LIST).get(GOODS_INFO + goodsId)), Goods.class);
//            if (goods == null) {
//                goods = getGoodsById(goodsId);
//                if (goods == null) {
//                    return new SeckillExcecution(goodsId, SecKillStatEnum.INNER_ERROR);
//                } else {
//                    redisTemplate.boundHashOps(GOODS_LIST).put(GOODS_INFO + goodsId, goods);
//                }
//            }
            //查缓存库存
            Integer number = JSON.parseObject(JSON.toJSONString(redisTemplate.boundValueOps(GOODS_NUMBER + goodsId).get()),Integer.class
            );
            if(number!=null&&number==0){
                return new SeckillExcecution(goodsId, SecKillStatEnum.END);
            }
            //加行级锁
            Goods goods = goodsDao.selectGoodsForUpdate(goodsId);
            Date date = new Date();
            //查库存
            int goodsNumber = goods.getGoodsNumber();
            if(goodsNumber==0){
                return new SeckillExcecution(goodsId, SecKillStatEnum.END);
            }
            //减库存
            goods.setGoodsNumber(goods.getGoodsNumber()-1);
            int update = goodsDao.update(goods, new LambdaQueryWrapper<Goods>()
                    .eq(Goods::getGoodsId, goodsId)
                    .lt(Goods::getStartTime, date)
                    .gt(Goods::getEndTime, date));
            if(update==1){
                //插入秒杀记录
                int insertUserSecKill = goodsDao.insertUserSecKill(userId, goodsId);
                if(insertUserSecKill==1){
                    //更新库存
                    redisTemplate.boundValueOps(GOODS_NUMBER + goodsId).decrement(1);
                    return new SeckillExcecution(goodsId,SecKillStatEnum.SUCCESS);
                }else {
                    return new SeckillExcecution(goodsId,SecKillStatEnum.REPEAT_KILL);
                }
            }else {
                return new SeckillExcecution(goodsId, SecKillStatEnum.END);
            }
        }

    }


    //乐观锁实现
    //并发时间短，并发数不高时，可能出现少卖
    @Transactional(rollbackFor = Exception.class)
    public SeckillExcecution executeSecKillDBOLock(long goodsId, Long userId, Long userPhone, String md5) throws ServerException {
        System.out.println("执行秒杀！");
        if (md5 == null || !md5.equals(getMD5(goodsId))) {
            throw new ServerException("seckill data rewrite");
        } else {
            if(userPhone==null||userId==null){
                return new SeckillExcecution(goodsId, SecKillStatEnum.LOGOUT);
            }
//            Goods goods = JSON.parseObject(JSON.toJSONString(redisTemplate.boundHashOps(GOODS_LIST).get(GOODS_INFO + goodsId)), Goods.class);
//            if (goods == null) {
//                goods = getGoodsById(goodsId);
//                if (goods == null) {
//                    return new SeckillExcecution(goodsId, SecKillStatEnum.INNER_ERROR);
//                } else {
//                    redisTemplate.boundHashOps(GOODS_LIST).put(GOODS_INFO + goodsId, goods);
//                }
//            }
            //加行级锁
            Goods goods = goodsDao.selectById(goodsId);
            Date date = new Date();
            //查库存
            int goodsNumber = goods.getGoodsNumber();
            if(goodsNumber==0){
                return new SeckillExcecution(goodsId, SecKillStatEnum.END);
            }
            //减库存
            int preVersion=goods.getVersion();
            goods.setVersion(preVersion+1);
            goods.setGoodsNumber(goods.getGoodsNumber()-1);
            int update = goodsDao.update(goods, new LambdaQueryWrapper<Goods>()
                    .eq(Goods::getGoodsId, goodsId)
                    .eq(Goods::getVersion,preVersion)
                    .lt(Goods::getStartTime, date)
                    .gt(Goods::getEndTime, date));
            if(update==1){
                //插入秒杀记录
                int insertUserSecKill = goodsDao.insertUserSecKill(userId, goodsId);
                if(insertUserSecKill==1){
                    return new SeckillExcecution(goodsId,SecKillStatEnum.SUCCESS);
                }else {
                    return new SeckillExcecution(goodsId,SecKillStatEnum.REPEAT_KILL);
                }
            }else {
                return new SeckillExcecution(goodsId, SecKillStatEnum.END);
            }
        }

    }

    //AOP+Redis分布式锁实现
    @Transactional(rollbackFor = Exception.class)
    public SeckillExcecution executeSecKillAOPRedisLock(long goodsId, Long userId, Long userPhone, String md5) throws ServerException {
        System.out.println("执行秒杀！");
        if (md5 == null || !md5.equals(getMD5(goodsId))) {
            throw new ServerException("seckill data rewrite");
        } else {
            if(userPhone==null||userId==null){
                return new SeckillExcecution(goodsId, SecKillStatEnum.LOGOUT);
            }
//            Goods goods = JSON.parseObject(JSON.toJSONString(redisTemplate.boundHashOps(GOODS_LIST).get(GOODS_INFO + goodsId)), Goods.class);
//            if (goods == null) {
//                goods = getGoodsById(goodsId);
//                if (goods == null) {
//                    return new SeckillExcecution(goodsId, SecKillStatEnum.INNER_ERROR);
//                } else {
//                    redisTemplate.boundHashOps(GOODS_LIST).put(GOODS_INFO + goodsId, goods);
//                }
//            }
            Goods goods = goodsDao.selectById(goodsId);
            Date date = new Date();
            //查库存
            int goodsNumber = goods.getGoodsNumber();
            if(goodsNumber==0){
                return new SeckillExcecution(goodsId, SecKillStatEnum.END);
            }
            //减库存
            goods.setGoodsNumber(goods.getGoodsNumber()-1);
            int update = goodsDao.update(goods, new LambdaQueryWrapper<Goods>()
                    .eq(Goods::getGoodsId, goodsId)
                    .lt(Goods::getStartTime, date)
                    .gt(Goods::getEndTime, date));
            if(update==1){
                //插入秒杀记录
                int insertUserSecKill = goodsDao.insertUserSecKill(userId, goodsId);
                if(insertUserSecKill==1){
                    return new SeckillExcecution(goodsId,SecKillStatEnum.SUCCESS);
                }else {
                    return new SeckillExcecution(goodsId,SecKillStatEnum.REPEAT_KILL);
                }
            }else {
                return new SeckillExcecution(goodsId, SecKillStatEnum.END);
            }
        }
    }


    //利用消息队列RabbitMQ执行秒杀，起到有效限流的作用
    public SeckillExcecution executeSecKillByMQ(long goodsId, Long userId, Long userPhone, String md5) throws ServerException {
        System.out.println("执行秒杀！");
        if (md5 == null || !md5.equals(getMD5(goodsId))) {
            throw new ServerException("seckill data rewrite");
        } else {
            if(userPhone==null||userId==null){
                return new SeckillExcecution(goodsId, SecKillStatEnum.LOGOUT);
            }
//            Goods goods = JSON.parseObject(JSON.toJSONString(redisTemplate.boundHashOps(GOODS_LIST).get(GOODS_INFO + goodsId)), Goods.class);
//            if (goods == null) {
//                goods = getGoodsById(goodsId);
//                if (goods == null) {
//                    return new SeckillExcecution(goodsId, SecKillStatEnum.INNER_ERROR);
//                } else {
//                    redisTemplate.boundHashOps(GOODS_LIST).put(GOODS_INFO + goodsId, goods);
//                }
//            }
            SecKillMsg secKillMsg = SecKillMsg.builder()
                    .goodsId(goodsId)
                    .userId(userId)
                    .userPhone(userPhone)
                    .md5(md5)
                    .build();
            secKillMQProducer.sendSecKillMessage(secKillMsg);
            return new SeckillExcecution(goodsId, SecKillStatEnum.ONGOING);
        }
    }

    @Override
    public void afterPropertiesSet(){
        //初始化Bean时，将数据库商品数据和库存存入redis中
        List<Goods> goodsList = goodsDao.selectList(null);
        for (Goods goods : goodsList) {
            redisTemplate.boundValueOps(GOODS_NUMBER + goods.getGoodsId()).set(goods.getGoodsNumber());
            redisTemplate.boundHashOps(GOODS_LIST).put(GOODS_INFO + goods.getGoodsId(), goods);
        }
    }
}
